package com.dh.spring.client.http;

import com.dh.spring.client.bootstrap.FileCoreServiceHandler;
import com.dh.spring.client.bootstrap.GlobalNIOClientBootstrap;
import com.dh.spring.client.bootstrap.GlobalNIOClientListenerDelegate;
import com.dh.spring.client.bootstrap.ServerSocketChannelHttpRequestHandler;
import com.dh.spring.client.file.common.FileCheckRunnable;
import com.dh.spring.client.file.common.SendEntity;
import com.dh.spring.client.file.download.SocketDecorate;
import com.dh.spring.common.BasicConstant;
import com.dh.spring.common.EPlatform;
import com.dh.spring.common.HttpResponse;
import com.dh.spring.common.OSinfo;
import com.dh.spring.util.BasicUtil;
import com.dh.spring.util.LocalTime;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.util.Iterator;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Auther: YSFY
 * @Date: 2020-09-13 21:26
 * @Pacage_name: com.dh.spring.client.http
 * @Project_Name: arthas-client
 * @Description: 获取文件服务端linux命令结果
 */

@Slf4j
@SuppressWarnings("all")
public class LinuxCommandHandler {
    private static AtomicInteger channelIndex = new AtomicInteger(1);

    public static HttpResponse linuxCommandHandler(RequestData requestData, HttpResponse httpResponse) throws InterruptedException, ExecutionException {
        FileCheckRunnable fileCheckRunnable = new FileCheckRunnable("NO FILE", requestData);
        Future<FileCheckRunnable.FileCheckResult> future = GlobalNIOClientBootstrap.executorService.submit(fileCheckRunnable);
        while (!future.isDone())
            TimeUnit.MILLISECONDS.sleep(10);
        FileCheckRunnable.FileCheckResult fileCheckResult = future.get();
        if(fileCheckResult.isHandler()) {
            log.info("[" + LocalTime.formatDate(LocalDateTime.now()) + "] LinuxCommandHandler | --> linux命令: linuxCommand = {} http请求处理完成", requestData.getExec() + String.valueOf(GlobalNIOClientListenerDelegate.SOCKET_REMOTE_PORT));
            httpResponse.getBodyData().append(future.get().getResult());
        }
        return httpResponse;
    }

    public static FileCheckRunnable.FileCheckResult executeLinuxCommandDownload(RequestData requestData, FileCheckRunnable.FileCheckResult fileCheckResult, FileExecStatus fileExecStatus) {
        SelectionKey selectionKey = null;
        String command = requestData.getExec() + String.valueOf(GlobalNIOClientListenerDelegate.SOCKET_REMOTE_PORT);
        fileCheckResult.setHandler(true); //TODO --> 设置当前线程处理成功
        try {
            LinkedBlockingQueue linkedBlockingQueue = ServerSocketChannelHttpRequestHandler.fileExecStatusMap.get(BasicConstant.LINUX_COMMAND);
            linkedBlockingQueue.add(fileExecStatus);
            log.info("[" + LocalTime.formatDate(LocalDateTime.now()) + "] LinuxCommandHandler | --> 成功加入向文件服务端发送执行的命令队列,当前待执行的linux命令数: linuxCommandCount = {}, 加入的命令为: command = {}", ServerSocketChannelHttpRequestHandler.fileExecStatusMap.get(BasicConstant.LINUX_COMMAND).size(), requestData.getExec() + String.valueOf(GlobalNIOClientListenerDelegate.SOCKET_REMOTE_PORT));

            selectionKey = getSocketChannelLinuxCommand(requestData, fileExecStatus);
            if (null == selectionKey) {
                fileCheckResult.setResult("未能针对linux命令 [ " + requestData.getExec() + String.valueOf(GlobalNIOClientListenerDelegate.SOCKET_REMOTE_PORT + " ] 分配SocketChannel执行连接 <-- \r\n"));
                ServerSocketChannelHttpRequestHandler.fileExecStatusMap.get(BasicConstant.LINUX_COMMAND).remove(fileExecStatus);
                return fileCheckResult;
            }

            //TODO --> linux命令执行连接文件服务端，此时等待文件服务端针对命令执行结果的返回
            ((SocketChannel) selectionKey.channel()).connect(new InetSocketAddress(InetAddress.getByName(GlobalNIOClientListenerDelegate.SOCKET_REMOTE_IP), GlobalNIOClientListenerDelegate.SOCKET_REMOTE_COMMAND_PORT));
            if (!linkedBlockingQueue.contains(fileExecStatus)) {
                log.info("[" + LocalTime.formatDate(LocalDateTime.now()) + "] LinuxCommandHandler | --> 当前命令加入待执行的命令队列失败，加入的命令为: linuxCommandCount = {}", ServerSocketChannelHttpRequestHandler.fileExecStatusMap.get(BasicConstant.LINUX_COMMAND).size(), command);
                fileCheckResult.setResult("当前命令加入待执行的命令队列失败,无法发送给文件服务器进行执行,command = " + command);
                return fileCheckResult;
            }

            Iterator iterator = linkedBlockingQueue.iterator();
            FileExecStatus temp = null;
            while (iterator.hasNext()) {
                if ((temp = ((FileExecStatus) iterator.next())) == fileExecStatus) {
                    while (temp.getResult() == null)
                        TimeUnit.MILLISECONDS.sleep(10);
                    fileCheckResult.setResult(temp.getResult().toString());
                    break;
                }
            }
            return fileCheckResult;
        } catch (Exception e) {
            FileCoreServiceHandler.closeSocketChannel(selectionKey, null, "LinuxCommandHandler","待执行的linux命令与服务端连接异常, error = " + e.getMessage(), BasicConstant.LINUX_COMMAND, "NO FILE", 0);
            fileCheckResult.setResult("[ " + command + " ] 待执行的linux命令与服务端连接异常, error = " + e.getMessage());
            return fileCheckResult;
        }
    }

    private static SelectionKey getSocketChannelLinuxCommand(RequestData requestData, FileExecStatus fileExecStatus) throws IOException {
        log.info("[" + LocalTime.formatDate(LocalDateTime.now()) + "] LinuxCommandHandler | --> 为当前的命令创建SocketChannel: linuxCommand = {}", requestData.getExec() + String.valueOf(GlobalNIOClientListenerDelegate.SOCKET_REMOTE_PORT));
        return decorateLinuxCommandSocketChannel(requestData, fileExecStatus);
    }

    //TODO --> 客户端封装文件上传请求socket连接
    private static SelectionKey decorateLinuxCommandSocketChannel(RequestData requestData, FileExecStatus fileExecStatus) throws IOException {
        //TODO --> 1、创建socketChannel上传通道
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.configureBlocking(false);
        SocketDecorate.getInstance(socketChannel.socket()).setCustomKeepAlive(true).setCustomTcpNoDelay(false).setSendBuffer(2048);;
        GlobalNIOClientBootstrap.selector.wakeup();
        SelectionKey selectionKey = socketChannel.register(GlobalNIOClientBootstrap.selector, SelectionKey.OP_CONNECT);

        String linuxCommand = requestData.getExec() + GlobalNIOClientListenerDelegate.SOCKET_REMOTE_PORT;

        //TODO --> 3、封装文件上传请求流数据以及实体类交由服务端校验
        byte[] bytes = linuxCommand.getBytes(Charset.forName("UTF-8"));
        ByteBuffer sendFileBuffer = ByteBuffer.allocateDirect(bytes.length);
        sendFileBuffer.put(bytes);
        SendEntity sendEntity = new SendEntity();
        sendEntity.setChannelName("[ " + linuxCommand + " ] linux命令通道 --- " + channelIndex.getAndIncrement());
        sendEntity.setHandlerName(BasicConstant.FILE_CORE_SERVICE_HANDLER);
        sendEntity.setByteBuffer(sendFileBuffer);
        sendEntity.setFileName("NO FILE");
        sendEntity.setExec(requestData.getExec());
        sendEntity.setPath("");
        sendEntity.setFileExecStatus(fileExecStatus);

        //TODO --> 4、上传文件信息实体注入当前可选键
        selectionKey.attach(sendEntity); //TODO
        GlobalNIOClientBootstrap.fileChannelsMap.put(selectionKey, null);//TODO 创建待上传文件FileChannel
        return selectionKey;
    }
}
